package utils

import (
	"bufio"
	"bytes"
	"fmt"
	"gin-web/library/errors"
	"github.com/ngaut/log"
	"golang.org/x/crypto/ssh"
	"io"
)

type sshConfig struct {
	user     string
	password string
	ip       string
	port     int
}

func NewSsh(user, password, ip string, port int) *sshConfig {
	return &sshConfig{
		user,
		password,
		ip,
		port,
	}
}

func (s *sshConfig) establishConnection() *ssh.Client {
	// 设置配置信息
	config := ssh.ClientConfig{
		User:            s.user,
		Auth:            []ssh.AuthMethod{ssh.Password(s.password)},
		HostKeyCallback: ssh.InsecureIgnoreHostKey(), // 用于简单的认证
	}

	// 与服务器建立连接
	client, err := ssh.Dial("tcp", fmt.Sprintf("%s:%d", s.ip, s.port), &config)
	if err != nil {
		log.Error(err)
		return nil
	}

	return client
}

func (s *sshConfig) Exec(cmd string) (error, bytes.Buffer) {
	var stdout, stderr bytes.Buffer
    client := s.establishConnection()
	if client == nil {
		return errors.ErrNoSshConnection, stdout
	}
	// 创建一个会话
	session, err := client.NewSession()
	if err != nil {
		return err, stdout
	}
	defer session.Close()

	// 设置输入输出
	session.Stdout = &stdout
	session.Stderr = &stderr
	err = session.Run(cmd)

	if err != nil {
		stderrStr := string(stderr.Bytes())
		if stderrStr == "" {
			stderrStr = err.Error()
		}
		return errors.New(stderrStr), stdout
	}
	return nil, stdout

}

//pipe方式 适合读取大批量日志场景和实时日志场景
func (s *sshConfig) ExecWithPipe(cmd string, errCh, outCh chan string) error {
    client := s.establishConnection()
	if client == nil {
		return errors.ErrNoSshConnection
	}
	// 创建一个会话
	session, err := client.NewSession()
	if err != nil {
		return err
	}
	defer session.Close()

	stdErr, _ := session.StderrPipe()
	stdOut, _ := session.StdoutPipe()

	go syncLog(stdOut, outCh)
	go syncLog(stdErr, errCh)
	err = session.Run(cmd)

	return err
}

func syncLog(r io.Reader, ch chan string) {
	scanner := bufio.NewScanner(r)
	for scanner.Scan() {
		line := scanner.Text()
		ch <- line
		log.Debug(line)
	}
	if err := scanner.Err(); err != nil {
		log.Errorf("error reading from stderr: %v", err)
	}
    close(ch)
}
